(function(win){
	var Deferred = win.Deferred = function(){
		this._fns = [];
	};

	win.Deferred.prototype = {
		_run:function(){
			if(!this._call || this._running) return;
			var defer = this;
			defer._running = true; setTimeout(function(){ defer._running = false; defer._next(); },0);
		},
		_push:function(fn){
			this._fns.push(fn);
			this._run();
		},
		call:function(result){
			if(this._call) throw "defer has called!";
			this._result = result; this._call = true;
			this._run();
		},
		_next:function(){
			var refer = this;
			while(refer._fns.length){
				this._fns[0](refer._result);
				this._fns.shift();
			};
		},
		next:function(fn){
			var defer = this, nextDeferred = new Deferred();

			defer._push(function(defer_result){
				var fnResult = fn(defer_result);
				if(fnResult instanceof Deferred){
					fnResult._push(function(fnResult_result){
						nextDeferred.call(fnResult_result);
					});
				} else {
					nextDeferred.call(fnResult);
				};
			});

			return nextDeferred;
		},
		wait:function(time){
			var defer = this, waitDeferred = new Deferred();

			defer._push(function(){
				setTimeout(function(){
					waitDeferred.call(defer._result); waitDeferred._next();
				},time);
			});
			return waitDeferred;
		},
		parallel:function(arr){
			if(arr.length == 0) return this;
			var defer = this, paraDeferred = new Deferred();

			defer._push(function(){
				var result = [], resultCount = 0;
				for(var i = 0; i < arr.length; i++){
					var item = arr[i];
					if(item instanceof Deferred){
						item._push(function(){
							result[i] = item._result; resultCount++;
							if(resultCount == arr.length){ paraDeferred.call(result); };
						});
					} else {
						result[i] = item; resultCount++;
						if(resultCount == arr.length){ paraDeferred.call(result); };
					};
				};
			});

			return paraDeferred;
		},
		loop:function(fn){
			var defer = this, loopDeferred = new Deferred(), loopIndex = 0;
			
			defer._push(function(defer_result){
				var fnResult = fn(loopIndex++, defer_result), callee = arguments.callee;
				if(fnResult instanceof Deferred){
					fnResult._push(function(fnDeferred_result){
						if(fnDeferred_result == true){
							setTimeout(function(){ callee(defer_result); },0);
						} else {
							loopDeferred.call(defer_result);
						};
					});
				} else {
					if(fnResult == true){
						setTimeout(function(){ callee(defer_result); },0);
					} else {
						loopDeferred.call(defer_result);
					};
				};
			});
			return loopDeferred;
		}
	};

	Deferred.define = function(obj){
		$.each(["parallel", "wait", "next", "loop"], function(i, item){
			obj[item] = function(){
				var defer = new Deferred();
				defer.call();
				return defer[item].apply(defer, arguments);
			};
		});
	};

	Deferred.define(win);
})(window);